package org.hsieh.tr.explore.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hsieh.tr.explore.exception.ExceptionHandler;
import org.hsieh.tr.explore.exception.SqlCommandExecuteException;
import org.hsieh.tr.explore.ui.ISqlResultView;

/**
 * Handler connection actions
 * 
 *@author hsieh 234aini@gmail.com
 * 
 */
public final class ConnectionWarpper implements IConnectionWarpper {

    private static final String REGEX = ";";

    private final Connection connection;

    List<ISqlResultView> resultViews = new ArrayList<ISqlResultView>();

    private ConnectionWarpper(Connection connection) {
	this.connection = connection;
    }

    public static IConnectionWarpper create(Connection connection) {
	return new ConnectionWarpper(connection);
    }

    private List<ISqlCommand> successCommands = new ArrayList<ISqlCommand>();

    private List<ISqlCommand> tempCommands = new ArrayList<ISqlCommand>();

    public void addResultView(ISqlResultView view) {
	if (!resultViews.contains(view)) {
	    resultViews.add(view);
	}
    }

    public void removeResultView(ISqlResultView view) {
	if (resultViews.contains(view)) {
	    resultViews.remove(view);
	}
    }

    public void addSqlCommand(ISqlCommand command) {
	if (!successCommands.contains(command)) {
	    successCommands.add(command);
	}
    }

    public void parseSQL(final String sqlText) {
	if (sqlText == null || sqlText.trim().equals("")) {
	    return; // do nothing
	}

	tempCommands.clear();
	String[] sqls = sqlText.split(REGEX);
	for (String sql : sqls) {
	    String sqlTemp = sql.trim();
	    if (sqlTemp.equals("") || sqlTemp.equals("\r\n")) {
		continue;
	    } else {
		ISqlCommand command = createCommand(sqlTemp);
		tempCommands.add(command);
	    }
	}
    }

    private ISqlCommand createCommand(String sql) {
	return SqlCommand.create(sql);
    }

    public void clearCommands() {
	successCommands.clear();
    }

    public List<ISqlCommand> getCommands() {
	return Collections.unmodifiableList(successCommands);
    }

    public void dispose() {
	try {
	    connection.commit();
	    connection.close();
	} catch (SQLException e) {
	    ExceptionHandler.handle(e);
	}

    }

    public void submit(String sql) throws Exception {
	tempCommands.clear();
	parseSQL(sql);
	if (!tempCommands.isEmpty()) {
	    for (ISqlCommand command : tempCommands) {
		Object object;
		try {
		    object = command.execute(getConnection());
		    if (!resultViews.isEmpty()) {
			for (ISqlResultView view : resultViews) {
			    toShowAndClear(object, view);
			}
		    }
		} catch (SqlCommandExecuteException e) {
		    // terminate
		    throw e;
		}
	    }
	}
    }

    /**
     * {@link ISqlResultView} is not charge of mantain the result object and
     * just show. We need to close result object sometimes.
     * 
     * @param object
     * @param view
     * @throws SQLException
     */
    private void toShowAndClear(Object object, ISqlResultView view)
	    throws SQLException {
	view.showResult(object);
	if (object instanceof ResultSet) {
	    ResultSet set = (ResultSet) object;
	    set.close();
	}
    }

    public void commit(String sql) throws Exception {
	submit(sql);
	getConnection().commit();
    }

    public void rollback() throws SQLException {
	getConnection().rollback();
    }

    public Connection getConnection() {
	return connection;
    }

    public void pureCommit() throws SQLException {
	getConnection().commit();
    }

}
